home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / non-ANSI / c-client / mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-26  |  48.1 KB  |  1,725 lines

  1. /*
  2.  * Program:    Mailbox Access routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 November 1989
  13.  * Last Edited:    26 May 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <time.h>
  42. #include "misc.h"
  43.  
  44.  
  45. /* c-client global data */
  46.  
  47. DRIVER *maildrivers = NIL;    /* list of mail drivers */
  48. DRIVER *inboxdriver = NIL;    /* INBOX mail driver */
  49. char *lhostn = NIL;        /* local host name */
  50. mailgets_t mailgets = NIL;    /* pointer to alternate gets function */
  51.                 /* mail cache manipulation function */
  52. mailcache_t mailcache = mm_cache;
  53.  
  54. /* Default limited get string
  55.  * Accepts: readin function pointer
  56.  *        stream to use
  57.  *        number of bytes
  58.  * Returns: string read in, truncated if necessary
  59.  *
  60.  * This is a sample mailgets routine.  It simply truncates any data larger
  61.  * than MAXMESSAGESIZE.  On most systems, you generally don't use a mailgets
  62.  * routine at all, but on some systems (e.g. DOS) it's required to prevent the
  63.  * application from crashing.  This one is filled in by the os driver for any
  64.  * OS that requires a mailgets routine and the main program has not already
  65.  * supplied one, generally in tcp_open().
  66.  */
  67.  
  68. char *mm_gets (f,stream,size)
  69.     readfn_t f;
  70.     void *stream;
  71.     unsigned long size;
  72. {
  73.   char *s;
  74.   char tmp[MAILTMPLEN+1];
  75.   unsigned long i,j = 0;
  76.                 /* truncate? */
  77.   if (i = (size > MAXMESSAGESIZE)) {
  78.     sprintf (tmp,"%ld character literal truncated to %ld characters",
  79.          size,MAXMESSAGESIZE);
  80.     mm_log (tmp,WARN);        /* warn user */
  81.     i = size - MAXMESSAGESIZE;    /* number of bytes of slop */
  82.     size = MAXMESSAGESIZE;    /* maximum length string we can read */
  83.   }
  84.   s = (char *) fs_get (size + 1);
  85.   *s = s[size] = '\0';        /* init in case getbuffer fails */
  86.   (*f) (stream,size,s);        /* get the literal */
  87.                 /* toss out everything after that */
  88.   while (i -= j) (*f) (stream,j = min ((long) MAILTMPLEN,i),tmp);
  89.   return s;
  90. }
  91.  
  92. /* Default mail cache handler
  93.  * Accepts: pointer to cache handle
  94.  *        message number
  95.  *        caching function
  96.  * Returns: cache data
  97.  */
  98.  
  99. void *mm_cache (stream,msgno,op)
  100.     MAILSTREAM *stream;
  101.     long msgno;
  102.     long op;
  103. {
  104.   size_t new;
  105.   void *ret = NIL;
  106.   long i = msgno - 1;
  107.   unsigned long j = stream->cachesize;
  108.   switch ((int) op) {        /* what function? */
  109.   case CH_INIT:            /* initialize cache */
  110.     if (stream->cachesize) {    /* flush old cache contents */
  111.       while (stream->cachesize) mm_cache (stream,stream->cachesize--,CH_FREE);
  112.       fs_give ((void **) &stream->cache.c);
  113.       stream->nmsgs = 0;    /* can't have any messages now */
  114.     }
  115.     break;
  116.   case CH_SIZE:            /* (re-)size the cache */
  117.     if (msgno > j) {        /* do nothing if size adequate */
  118.       new = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  119.       if (stream->cache.c) fs_resize ((void **) &stream->cache.c,new);
  120.       else stream->cache.c = (void **) fs_get (new);
  121.                 /* init cache */
  122.       while (j < stream->cachesize) stream->cache.c[j++] = NIL;
  123.     }
  124.     break;
  125.   case CH_MAKELELT:        /* return long elt, make if necessary */
  126.     if (!stream->cache.c[i]) {    /* have one already? */
  127.                 /* no, instantiate it */
  128.       stream->cache.l[i] = (LONGCACHE *) fs_get (sizeof (LONGCACHE));
  129.       memset (&stream->cache.l[i]->elt,0,sizeof (MESSAGECACHE));
  130.       stream->cache.l[i]->elt.lockcount = 1;
  131.       stream->cache.l[i]->elt.msgno = msgno;
  132.       stream->cache.l[i]->env = NIL;
  133.       stream->cache.l[i]->body = NIL;
  134.     }
  135.                 /* drop in to CH_LELT */
  136.   case CH_LELT:            /* return long elt */
  137.     ret = stream->cache.c[i];    /* return void version */
  138.     break;
  139.  
  140.   case CH_MAKEELT:        /* return short elt, make if necessary */
  141.     if (!stream->cache.c[i]) {    /* have one already? */
  142.       if (stream->scache) {    /* short cache? */
  143.     stream->cache.s[i] = (MESSAGECACHE *) fs_get (sizeof(MESSAGECACHE));
  144.     memset (stream->cache.s[i],0,sizeof (MESSAGECACHE));
  145.     stream->cache.s[i]->lockcount = 1;
  146.     stream->cache.s[i]->msgno = msgno;
  147.       }
  148.       else mm_cache (stream,msgno,CH_MAKELELT);
  149.     }
  150.                 /* drop in to CH_ELT */
  151.   case CH_ELT:            /* return short elt */
  152.     ret = stream->cache.c[i] && !stream->scache ?
  153.       (void *) &stream->cache.l[i]->elt : stream->cache.c[i];
  154.     break;
  155.   case CH_FREE:            /* free (l)elt */
  156.     if (stream->scache) mail_free_elt (&stream->cache.s[i]);
  157.     else mail_free_lelt (&stream->cache.l[i]);
  158.     break;
  159.   case CH_EXPUNGE:        /* expunge cache slot */
  160.                 /* slide down remainder of cache */
  161.     for (i = msgno; i < stream->nmsgs; ++i)
  162.       if (stream->cache.c[i-1] = stream->cache.c[i])
  163.     ((MESSAGECACHE *) mm_cache (stream,i,CH_ELT))->msgno = i;
  164.     stream->cache.c[stream->nmsgs-1] = NIL;
  165.     break;
  166.   default:
  167.     fatal ("Bad mm_cache op");
  168.     break;
  169.   }
  170.   return ret;
  171. }
  172.  
  173. /* Dummy string driver for complete in-memory strings */
  174.  
  175. STRINGDRIVER mail_string = {
  176.   mail_string_init,        /* initialize string structure */
  177.   mail_string_next,        /* get next byte in string structure */
  178.   mail_string_setpos        /* set position in string structure */
  179. };
  180.  
  181.  
  182. /* Initialize mail string structure for in-memory string
  183.  * Accepts: string structure
  184.  *        pointer to string
  185.  *        size of string
  186.  */
  187.  
  188. void mail_string_init (s,data,size)
  189.     STRING *s;
  190.     void *data;
  191.     unsigned long size;
  192. {
  193.                 /* set initial string pointers */
  194.   s->chunk = s->curpos = (char *) (s->data = data);
  195.                 /* and sizes */
  196.   s->size = s->chunksize = s->cursize = size;
  197.   s->data1 = s->offset = 0;    /* never any offset */
  198. }
  199.  
  200. /* Get next character from string
  201.  * Accepts: string structure
  202.  * Returns: character, string structure chunk refreshed
  203.  */
  204.  
  205. char mail_string_next (s)
  206.     STRING *s;
  207. {
  208.   return *s->curpos++;        /* return the last byte */
  209. }
  210.  
  211.  
  212. /* Set string pointer position
  213.  * Accepts: string structure
  214.  *        new position
  215.  */
  216.  
  217. void mail_string_setpos (s,i)
  218.     STRING *s;
  219.     unsigned long i;
  220. {
  221.   s->curpos = s->chunk + i;    /* set new position */
  222.   s->cursize = s->chunksize - i;/* and new size */
  223. }
  224.  
  225. /* Mail routines
  226.  *
  227.  *  mail_xxx routines are the interface between this module and the outside
  228.  * world.  Only these routines should be referenced by external callers.
  229.  *
  230.  *  Note that there is an important difference between a "sequence" and a
  231.  * "message #" (msgno).  A sequence is a string representing a sequence in
  232.  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
  233.  * is a single integer.
  234.  *
  235.  */
  236.  
  237. /* Mail link driver
  238.  * Accepts: driver to add to list
  239.  */
  240.  
  241. void mail_link (driver)
  242.     DRIVER *driver;
  243. {
  244.   DRIVER **d = &maildrivers;
  245.   while (*d) d = &(*d)->next;    /* find end of list of drivers */
  246.   *d = driver;            /* put driver at the end */
  247.   driver->next = NIL;        /* this driver is the end of the list */
  248. }
  249.  
  250.  
  251. /* Mail manipulate driver parameters
  252.  * Accepts: mail stream
  253.  *        function code
  254.  *        function-dependent value
  255.  * Returns: function-dependent return value
  256.  */
  257.  
  258. void *mail_parameters (stream,function,value)
  259.     MAILSTREAM *stream;
  260.     long function;
  261.     void *value;
  262. {
  263.   return stream->dtb ? (stream->dtb->parameters) (function,value) : NIL;
  264. }
  265.  
  266. /* Mail find list of subscribed mailboxes
  267.  * Accepts: mail stream
  268.  *        pattern to search
  269.  */
  270.  
  271. void mail_find (stream,pat)
  272.     MAILSTREAM *stream;
  273.     char *pat;
  274. {
  275.   DRIVER *d = maildrivers;
  276.                 /* if have a stream, do it for that stream */
  277.   if (stream && stream->dtb) (*stream->dtb->find) (stream,pat);
  278.                 /* otherwise do for all DTB's */
  279.   else do (d->find) (NIL,pat);
  280.   while (d = d->next);        /* until at the end */
  281. }
  282.  
  283.  
  284. /* Mail find list of subscribed bboards
  285.  * Accepts: mail stream
  286.  *        pattern to search
  287.  */
  288.  
  289. void mail_find_bboards (stream,pat)
  290.     MAILSTREAM *stream;
  291.     char *pat;
  292. {
  293.   DRIVER *d = maildrivers;
  294.   if (stream && stream->dtb) (*stream->dtb->find_bboard) (stream,pat);
  295.   else do (d->find_bboard) (NIL,pat);
  296.   while (d = d->next);        /* until at the end */
  297. }
  298.  
  299. /* Mail find list of all mailboxes
  300.  * Accepts: mail stream
  301.  *        pattern to search
  302.  */
  303.  
  304. void mail_find_all (stream,pat)
  305.     MAILSTREAM *stream;
  306.     char *pat;
  307. {
  308.   DRIVER *d = maildrivers;
  309.                 /* if have a stream, do it for that stream */
  310.   if (stream && stream->dtb) (*stream->dtb->find_all) (stream,pat);
  311.                 /* otherwise do for all DTB's */
  312.   else do (d->find_all) (NIL,pat);
  313.   while (d = d->next);        /* until at the end */
  314. }
  315.  
  316.  
  317. /* Mail find list of all bboards
  318.  * Accepts: mail stream
  319.  *        pattern to search
  320.  */
  321.  
  322. void mail_find_all_bboard (stream,pat)
  323.     MAILSTREAM *stream;
  324.     char *pat;
  325. {
  326.   DRIVER *d = maildrivers;
  327.                 /* if have a stream, do it for that stream */
  328.   if (stream && stream->dtb) (*stream->dtb->find_all_bboard) (stream,pat);
  329.                 /* otherwise do for all DTB's */
  330.   else do (d->find_all_bboard) (NIL,pat);
  331.   while (d = d->next);        /* until at the end */
  332. }
  333.  
  334. /* Mail validate mailbox name
  335.  * Accepts: MAIL stream
  336.  *        mailbox name
  337.  *        purpose string for error message
  338.  *        flag indicating this is not an open
  339.  * Return: driver factory on success, NIL on failure
  340.  */
  341.  
  342. DRIVER *mail_valid (stream,mailbox,purpose,nopen)
  343.     MAILSTREAM *stream;
  344.     char *mailbox;
  345.     char *purpose;
  346.     long nopen;
  347. {
  348.   char tmp[MAILTMPLEN];
  349.   DRIVER *factory;
  350.   for (factory = maildrivers; factory && !(*factory->valid) (mailbox);
  351.        factory = factory->next);
  352.                 /* must match stream and not be dummy */
  353.   if (factory && ((stream && (stream->dtb != factory)) ||
  354.           (nopen && !strcmp (factory->name,"dummy")))) factory = NIL;
  355.   if (!factory && purpose) {    /* if want an error message */
  356.     sprintf (tmp,"Can't %s %s: no such mailbox",purpose,mailbox);
  357.     mm_log (tmp,ERROR);
  358.   }
  359.   return factory;        /* return driver factory */
  360. }
  361.  
  362.  
  363. /* Mail validate network mailbox name
  364.  * Accepts: mailbox name
  365.  *        mailbox driver to validate against
  366.  *        pointer to where to return host name if non-NIL
  367.  *        pointer to where to return mailbox name if non-NIL
  368.  * Returns: driver on success, NIL on failure
  369.  */
  370.  
  371. DRIVER *mail_valid_net (name,drv,host,mailbox)
  372.     char *name;
  373.     DRIVER *drv;
  374.     char *host;
  375.     char *mailbox;
  376. {
  377.   NETMBX mb;
  378.   if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
  379.     return NIL;
  380.   if (host) strcpy (host,mb.host);
  381.   if (mailbox) strcpy (mailbox,mb.mailbox);
  382.   return drv;
  383. }
  384.  
  385. /* Mail validate network mailbox name
  386.  * Accepts: mailbox name
  387.  *        NETMBX structure to return values
  388.  * Returns: T on success, NIL on failure
  389.  */
  390.  
  391. long mail_valid_net_parse (name,mb)
  392.     char *name;
  393.     NETMBX *mb;
  394. {
  395.   long i;
  396.   char c,*s,*t,*v;
  397.   mb->port = 0;            /* initialize structure */
  398.   *mb->host = *mb->mailbox = *mb->service = '\0';
  399.   mb->anoflag = NIL;        /* not (yet) anonymous */
  400.                 /* check if bboard */
  401.   if (mb->bbdflag = (*name == '*') ? T : NIL) name++;
  402.                 /* have host specification? */
  403.   if (!(*name == '{' && (t = strchr (s = name+1,'}')) && (i = t - s)))
  404.     return NIL;            /* not valid host specification */
  405.   strncpy (mb->host,s,i);    /* set host name */
  406.   mb->host[i] = '\0';        /* tie it off */
  407.   strcpy (mb->mailbox,t+1);    /* set mailbox name */
  408.                 /* any switches or port specification? */
  409.   if (t = strpbrk (mb->host,"/:")) {
  410.     c = *t;            /* yes, remember delimiter */
  411.     *t++ = '\0';        /* tie off host name */
  412.     lcase (t);            /* coerce remaining stuff to lowercase */
  413.     do switch (c) {        /* act based upon the character */
  414.     case ':':            /* port specification */
  415.       if (mb->port || ((mb->port = strtol (t,&t,10)) <= 0)) return NIL;
  416.       c = t ? *t++ : '\0';    /* get delimiter, advance pointer */
  417.       break;
  418.  
  419.     case '/':            /* switch */
  420.                 /* find delimiter */
  421.       if (t = strpbrk (s = t,"/:=")) {
  422.     c = *t;            /* remember delimiter for later */
  423.     *t++ = '\0';        /* tie off switch name */
  424.       }
  425.       else c = '\0';        /* no delimiter */
  426.       if (c == '=') {        /* parse switches which take arguments */
  427.     if (t = strpbrk (v = t,"/:")) {
  428.       c = *t;        /* remember delimiter for later */
  429.       *t++ = '\0';        /* tie off switch name */
  430.     }
  431.     else c = '\0';        /* no delimiter */
  432.     if (!strcmp (s,"service")) {
  433.       if (*mb->service) return NIL;
  434.       else strcpy (mb->service,v);
  435.     }
  436.     else return NIL;    /* invalid argument switch */
  437.       }
  438.       else {            /* non-argument switch */
  439.     if (!strcmp (s,"anonymous")) mb->anoflag = T;
  440.     else if (!strcmp (s,"imap2") || !strcmp (s,"nntp")) {
  441.       if (*mb->service) return NIL;
  442.       else strcpy (mb->service,s);
  443.     }
  444.     else return NIL;    /* invalid non-argument switch */
  445.       }
  446.       break;
  447.     default:            /* anything else is bogus */
  448.       return NIL;
  449.     } while (c);        /* see if anything more to parse */
  450.   }
  451.                 /* default service name */
  452.   if (!*mb->service) strcpy (mb->service,"imap2");
  453.   return T;
  454. }
  455.  
  456. /* Mail subscribe to mailbox
  457.  * Accepts: mail stream
  458.  *        mailbox to add to subscription list
  459.  * Returns: T on success, NIL on failure
  460.  */
  461.  
  462. long mail_subscribe (stream,mailbox)
  463.     MAILSTREAM *stream;
  464.     char *mailbox;
  465. {
  466.   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox",LONGT);
  467.   return factory ? (*factory->subscribe) (stream,mailbox) : NIL;
  468. }
  469.  
  470.  
  471. /* Mail unsubscribe to mailbox
  472.  * Accepts: mail stream
  473.  *        mailbox to delete from subscription list
  474.  * Returns: T on success, NIL on failure
  475.  */
  476.  
  477. long mail_unsubscribe (stream,mailbox)
  478.     MAILSTREAM *stream;
  479.     char *mailbox;
  480. {
  481.  DRIVER *factory = mail_valid (stream,mailbox,"unsubscribe to mailbox",LONGT);
  482.   return factory ? (*factory->unsubscribe) (stream,mailbox) : NIL;
  483. }
  484.  
  485.  
  486. /* Mail subscribe to bboard
  487.  * Accepts: mail stream
  488.  *        bboard to add to subscription list
  489.  * Returns: T on success, NIL on failure
  490.  */
  491.  
  492. long mail_subscribe_bboard (stream,mailbox)
  493.     MAILSTREAM *stream;
  494.     char *mailbox;
  495. {
  496.   char tmp[MAILTMPLEN];
  497.   DRIVER *factory;
  498.   sprintf (tmp,"*%s",mailbox);
  499.   return (factory = mail_valid (stream,tmp,"subscribe to bboard",LONGT)) ?
  500.     (*factory->subscribe_bboard) (stream,mailbox) : NIL;
  501. }
  502.  
  503.  
  504. /* Mail unsubscribe to bboard
  505.  * Accepts: mail stream
  506.  *        bboard to delete from subscription list
  507.  * Returns: T on success, NIL on failure
  508.  */
  509.  
  510. long mail_unsubscribe_bboard (stream,mailbox)
  511.     MAILSTREAM *stream;
  512.     char *mailbox;
  513. {
  514.   char tmp[MAILTMPLEN];
  515.   DRIVER *factory;
  516.   sprintf (tmp,"*%s",mailbox);
  517.   return (factory = mail_valid (stream,tmp,"unsubscribe to bboard",LONGT)) ?
  518.     (*factory->unsubscribe_bboard) (stream,mailbox) : NIL;
  519. }
  520.  
  521. /* Mail create mailbox
  522.  * Accepts: mail stream
  523.  *        mailbox name to create
  524.  * Returns: T on success, NIL on failure
  525.  */
  526.  
  527. long mail_create (stream,mailbox)
  528.     MAILSTREAM *stream;
  529.     char *mailbox;
  530. {
  531.   int localp = *mailbox != '{';
  532.   char tmp[MAILTMPLEN];
  533.   if (!stream &&        /* guess at driver if stream not specified */
  534.       !(stream = mail_open (NIL,localp ? "INBOX" : mailbox,OP_PROTOTYPE))) {
  535.     sprintf (tmp,"Can't create mailbox %s: indeterminate format",mailbox);
  536.     mm_log (tmp,ERROR);
  537.     return NIL;
  538.   }
  539.                 /* must not already exist if local */
  540.   if (localp && mail_valid (stream,mailbox,NIL,LONGT)) {
  541.     sprintf (tmp,"Can't create mailbox %s: mailbox already exists",mailbox);
  542.     mm_log (tmp,ERROR);
  543.     return NIL;
  544.   }
  545.   return stream->dtb ? (*stream->dtb->create) (stream,mailbox) : NIL;
  546. }
  547.  
  548.  
  549. /* Mail delete mailbox
  550.  * Accepts: mail stream
  551.  *        mailbox name to delete
  552.  * Returns: T on success, NIL on failure
  553.  */
  554.  
  555. long mail_delete (stream,mailbox)
  556.     MAILSTREAM *stream;
  557.     char *mailbox;
  558. {
  559.   DRIVER *factory = mail_valid (stream,mailbox,"delete mailbox",LONGT);
  560.   return factory ? (*factory->delete) (stream,mailbox) : NIL;
  561. }
  562.  
  563.  
  564. /* Mail rename mailbox
  565.  * Accepts: mail stream
  566.  *        old mailbox name
  567.  *        new mailbox name
  568.  * Returns: T on success, NIL on failure
  569.  */
  570.  
  571. long mail_rename (stream,old,new)
  572.     MAILSTREAM *stream;
  573.     char *old;
  574.     char *new;
  575. {
  576.   char tmp[MAILTMPLEN];
  577.   DRIVER *factory = mail_valid (stream,old,"rename mailbox",LONGT);
  578.   if ((*old != '{') && mail_valid (NIL,new,NIL,LONGT)) {
  579.     sprintf (tmp,"Can't rename to mailbox %s: mailbox already exists",new);
  580.     mm_log (tmp,ERROR);
  581.     return NIL;
  582.   }
  583.   return factory ? (*factory->rename) (stream,old,new) : NIL;
  584. }
  585.  
  586. /* Mail open
  587.  * Accepts: candidate stream for recycling
  588.  *        mailbox name
  589.  *        open options
  590.  * Returns: stream to use on success, NIL on failure
  591.  */
  592.  
  593. MAILSTREAM *mail_open (stream,name,options)
  594.     MAILSTREAM *stream;
  595.     char *name;
  596.     long options;
  597. {
  598.   DRIVER *factory = mail_valid (NIL,name,(!(stream && stream->silent)) ?
  599.                 "open mailbox" : NIL,(long) NIL);
  600.   if (factory) {        /* must have a factory */
  601.     if (!stream) {        /* instantiate stream if none to recycle */
  602.       if (options & OP_PROTOTYPE) return (*factory->open) (NIL);
  603.       stream = (MAILSTREAM *) fs_get (sizeof (MAILSTREAM));
  604.                 /* initialize stream */
  605.       memset ((void *) stream,0,sizeof (MAILSTREAM));
  606.       stream->dtb = factory;    /* set dispatch */
  607.                 /* set mailbox name */
  608.       stream->mailbox = cpystr (name);
  609.                 /* initialize cache */
  610.       (*mailcache) (stream,(long) 0,CH_INIT);
  611.     }
  612.     else {            /* close driver if different from factory */
  613.       if (stream->dtb != factory) {
  614.     if (stream->dtb) (*stream->dtb->close) (stream);
  615.     stream->dtb = factory;    /* establish factory as our driver */
  616.     stream->local = NIL;    /* flush old driver's local data */
  617.     mail_free_cache (stream);
  618.       }
  619.                 /* clean up old mailbox name for recycling */
  620.       if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  621.       stream->mailbox = cpystr (name);
  622.     }
  623.     stream->lock = NIL;        /* initialize lock and options */
  624.     stream->debug = (options & OP_DEBUG) ? T : NIL;
  625.     stream->readonly = (options & OP_READONLY) ? T : NIL;
  626.     stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
  627.     stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
  628.     stream->silent = (options & OP_SILENT) ? T : NIL;
  629.     stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
  630.                 /* have driver open, flush if failed */
  631.     if (!(*factory->open) (stream)) stream = mail_close (stream);
  632.   }
  633.   return stream;        /* return the stream */
  634. }
  635.  
  636. /* Mail close
  637.  * Accepts: mail stream
  638.  * Returns: NIL
  639.  */
  640.  
  641. MAILSTREAM *mail_close (stream)
  642.     MAILSTREAM *stream;
  643. {
  644.   if (stream) {            /* make sure argument given */
  645.                 /* do the driver's close action */
  646.     if (stream->dtb) (*stream->dtb->close) (stream);
  647.     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  648.     stream->sequence++;        /* invalidate sequence */
  649.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  650.     mail_free_cache (stream);    /* finally free the stream's storage */
  651.     if (!stream->use) fs_give ((void **) &stream);
  652.   }
  653.   return NIL;
  654. }
  655.  
  656. /* Mail make handle
  657.  * Accepts: mail stream
  658.  * Returns: handle
  659.  *
  660.  *  Handles provide a way to have multiple pointers to a stream yet allow the
  661.  * stream's owner to nuke it or recycle it.
  662.  */
  663.  
  664. MAILHANDLE *mail_makehandle (stream)
  665.     MAILSTREAM *stream;
  666. {
  667.   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
  668.   handle->stream = stream;    /* copy stream */
  669.                 /* and its sequence */
  670.   handle->sequence = stream->sequence;
  671.   stream->use++;        /* let stream know another handle exists */
  672.   return handle;
  673. }
  674.  
  675.  
  676. /* Mail release handle
  677.  * Accepts: Mail handle
  678.  */
  679.  
  680. void mail_free_handle (handle)
  681.     MAILHANDLE **handle;
  682. {
  683.   MAILSTREAM *s;
  684.   if (*handle) {        /* only free if exists */
  685.                 /* resign stream, flush unreferenced zombies */
  686.     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
  687.     fs_give ((void **) handle);    /* now flush the handle */
  688.   }
  689. }
  690.  
  691.  
  692. /* Mail get stream handle
  693.  * Accepts: Mail handle
  694.  * Returns: mail stream or NIL if stream gone
  695.  */
  696.  
  697. MAILSTREAM *mail_stream (handle)
  698.     MAILHANDLE *handle;
  699. {
  700.   MAILSTREAM *s = handle->stream;
  701.   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
  702. }
  703.  
  704. /* Mail fetch long cache element
  705.  * Accepts: mail stream
  706.  *        message # to fetch
  707.  * Returns: long cache element of this message
  708.  * Can also be used to create cache elements for new messages.
  709.  */
  710.  
  711. LONGCACHE *mail_lelt (stream,msgno)
  712.     MAILSTREAM *stream;
  713.     long msgno;
  714. {
  715.   if (stream->scache) fatal ("Short cache in mail_lelt");
  716.                 /* be sure it the cache is large enough */
  717.   (*mailcache) (stream,msgno,CH_SIZE);
  718.   return (LONGCACHE *) (*mailcache) (stream,msgno,CH_MAKELELT);
  719. }
  720.  
  721.  
  722. /* Mail fetch cache element
  723.  * Accepts: mail stream
  724.  *        message # to fetch
  725.  * Returns: cache element of this message
  726.  * Can also be used to create cache elements for new messages.
  727.  */
  728.  
  729. MESSAGECACHE *mail_elt (stream,msgno)
  730.     MAILSTREAM *stream;
  731.     long msgno;
  732. {
  733.   if (msgno < 1) fatal ("Bad msgno in mail_elt");
  734.                 /* be sure it the cache is large enough */
  735.   (*mailcache) (stream,msgno,CH_SIZE);
  736.   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
  737. }
  738.  
  739. /* Mail fetch fast information
  740.  * Accepts: mail stream
  741.  *        sequence
  742.  *
  743.  * Generally, mail_fetchstructure is preferred
  744.  */
  745.  
  746. void mail_fetchfast (stream,sequence)
  747.     MAILSTREAM *stream;
  748.     char *sequence;
  749. {
  750.                   /* do the driver's action */
  751.   if (stream->dtb) (*stream->dtb->fetchfast) (stream,sequence);
  752. }
  753.  
  754.  
  755. /* Mail fetch flags
  756.  * Accepts: mail stream
  757.  *        sequence
  758.  */
  759.  
  760. void mail_fetchflags (stream,sequence)
  761.     MAILSTREAM *stream;
  762.     char *sequence;
  763. {
  764.                   /* do the driver's action */
  765.   if (stream->dtb) (*stream->dtb->fetchflags) (stream,sequence);
  766. }
  767.  
  768.  
  769. /* Mail fetch message structure
  770.  * Accepts: mail stream
  771.  *        message # to fetch
  772.  *        pointer to return body
  773.  * Returns: envelope of this message, body returned in body value
  774.  *
  775.  * Fetches the "fast" information as well
  776.  */
  777.  
  778. ENVELOPE *mail_fetchstructure (stream,msgno,body)
  779.     MAILSTREAM *stream;
  780.     long msgno;
  781.     BODY **body;
  782. {
  783.   if (msgno < 1 || msgno > stream->nmsgs)
  784.     fatal ("Bad msgno in mail_fetchstructure");
  785.                   /* do the driver's action */
  786.   return stream->dtb ? (*stream->dtb->fetchstructure) (stream,msgno,body) :NIL;
  787. }
  788.  
  789. /* Mail fetch message header
  790.  * Accepts: mail stream
  791.  *        message # to fetch
  792.  * Returns: message header in RFC822 format
  793.  */
  794.  
  795. char *mail_fetchheader (stream,msgno)
  796.     MAILSTREAM *stream;
  797.     long msgno;
  798. {
  799.   if (msgno < 1 || msgno > stream->nmsgs)
  800.     fatal ("Bad msgno in mail_fetchheader");
  801.                   /* do the driver's action */
  802.   return stream->dtb ? (*stream->dtb->fetchheader) (stream,msgno) : "";
  803. }
  804.  
  805.  
  806. /* Mail fetch message text (only)
  807.     body only;
  808.  * Accepts: mail stream
  809.  *        message # to fetch
  810.  * Returns: message text in RFC822 format
  811.  */
  812.  
  813. char *mail_fetchtext (stream,msgno)
  814.     MAILSTREAM *stream;
  815.     long msgno;
  816. {
  817.   if (msgno < 1 || msgno > stream->nmsgs)
  818.     fatal ("Bad msgno in mail_fetchtext");
  819.                   /* do the driver's action */
  820.   return stream->dtb ? (*stream->dtb->fetchtext) (stream,msgno) : "";
  821. }
  822.  
  823.  
  824. /* Mail fetch message body part text
  825.  * Accepts: mail stream
  826.  *        message # to fetch
  827.  *        section specifier (#.#.#...#)
  828.  *        pointer to returned length
  829.  * Returns: pointer to section of message body
  830.  */
  831.  
  832. char *mail_fetchbody (stream,m,sec,len)
  833.     MAILSTREAM *stream;
  834.     long m;
  835.     char *sec;
  836.     unsigned long *len;
  837. {
  838.   if (m < 1 || m > stream->nmsgs) fatal ("Bad msgno in mail_fetchbody");
  839.                   /* do the driver's action */
  840.   return stream->dtb ? (*stream->dtb->fetchbody) (stream,m,sec,len) : "";
  841. }
  842.  
  843. /* Mail fetch From string for menu
  844.  * Accepts: destination string
  845.  *        mail stream
  846.  *        message # to fetch
  847.  *        desired string length
  848.  * Returns: string of requested length
  849.  */
  850.  
  851. void mail_fetchfrom (s,stream,msgno,length)
  852.     char *s;
  853.     MAILSTREAM *stream;
  854.     long msgno;
  855.     long length;
  856. {
  857.   char *t;
  858.   char tmp[MAILTMPLEN];
  859.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  860.   memset (s,' ',length);    /* fill it with spaces */
  861.   s[length] = '\0';        /* tie off with null */
  862.                 /* get first from address from envelope */
  863.   if (env && env->from) {    /* if a personal name exists use it */
  864.     if (!(t = env->from->personal))
  865.       sprintf (t = tmp,"%s@%s",env->from->mailbox,env->from->host);
  866.     memcpy (s,t,min (length,(long) strlen (t)));
  867.   }
  868. }
  869.  
  870.  
  871. /* Mail fetch Subject string for menu
  872.  * Accepts: destination string
  873.  *        mail stream
  874.  *        message # to fetch
  875.  *        desired string length
  876.  * Returns: string of no more than requested length
  877.  */
  878.  
  879. void mail_fetchsubject (s,stream,msgno,length)
  880.     char *s;
  881.     MAILSTREAM *stream;
  882.     long msgno;
  883.     long length;
  884. {
  885.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  886.   memset (s,'\0',length+1);
  887.                 /* copy subject from envelope */
  888.   if (env && env->subject) strncpy (s,env->subject,length);
  889.   else *s = ' ';        /* if no subject then just a space */
  890. }
  891.  
  892. /* Mail set flag
  893.  * Accepts: mail stream
  894.  *        sequence
  895.  *        flag(s)
  896.  */
  897.  
  898. void mail_setflag (stream,sequence,flag)
  899.     MAILSTREAM *stream;
  900.     char *sequence;
  901.     char *flag;
  902. {
  903.                   /* do the driver's action */
  904.   if (stream->dtb) (*stream->dtb->setflag) (stream,sequence,flag);
  905. }
  906.  
  907.  
  908. /* Mail clear flag
  909.  * Accepts: mail stream
  910.  *        sequence
  911.  *        flag(s)
  912.  */
  913.  
  914. void mail_clearflag (stream,sequence,flag)
  915.     MAILSTREAM *stream;
  916.     char *sequence;
  917.     char *flag;
  918. {
  919.                   /* do the driver's action */
  920.   if (stream->dtb) (*stream->dtb->clearflag) (stream,sequence,flag);
  921. }
  922.  
  923.  
  924. /* Mail search for messages
  925.  * Accepts: mail stream
  926.  *        search criteria
  927.  */
  928.  
  929. void mail_search (stream,criteria)
  930.     MAILSTREAM *stream;
  931.     char *criteria;
  932. {
  933.   long i = 1;
  934.   while (i <= stream->nmsgs) mail_elt (stream,i++)->searched = NIL;
  935.                   /* do the driver's action */
  936.   if (stream->dtb) (*stream->dtb->search) (stream,criteria);
  937. }
  938.  
  939.  
  940. /* Mail ping mailbox
  941.  * Accepts: mail stream
  942.  * Returns: stream if still open else NIL
  943.  */
  944.  
  945. long mail_ping (stream)
  946.     MAILSTREAM *stream;
  947. {
  948.                   /* do the driver's action */
  949.   return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
  950. }
  951.  
  952. /* Mail check mailbox
  953.  * Accepts: mail stream
  954.  */
  955.  
  956. void mail_check (stream)
  957.     MAILSTREAM *stream;
  958. {
  959.                   /* do the driver's action */
  960.   if (stream->dtb) (*stream->dtb->check) (stream);
  961. }
  962.  
  963.  
  964. /* Mail expunge mailbox
  965.  * Accepts: mail stream
  966.  */
  967.  
  968. void mail_expunge (stream)
  969.     MAILSTREAM *stream;
  970. {
  971.                   /* do the driver's action */
  972.   if (stream->dtb) (*stream->dtb->expunge) (stream);
  973. }
  974.  
  975. /* Mail copy message(s)
  976.     s;
  977.  * Accepts: mail stream
  978.  *        sequence
  979.  *        destination mailbox
  980.  */
  981.  
  982. long mail_copy (stream,sequence,mailbox)
  983.     MAILSTREAM *stream;
  984.     char *sequence;
  985.     char *mailbox;
  986. {
  987.                   /* do the driver's action */
  988.   return stream->dtb ? (*stream->dtb->copy) (stream,sequence,mailbox) : NIL;
  989. }
  990.  
  991.  
  992. /* Mail move message(s)
  993.     s;
  994.  * Accepts: mail stream
  995.  *        sequence
  996.  *        destination mailbox
  997.  */
  998.  
  999. long mail_move (stream,sequence,mailbox)
  1000.     MAILSTREAM *stream;
  1001.     char *sequence;
  1002.     char *mailbox;
  1003. {
  1004.                   /* do the driver's action */
  1005.   return stream->dtb ? (*stream->dtb->move) (stream,sequence,mailbox) : NIL;
  1006. }
  1007.  
  1008.  
  1009. /* Mail append message string
  1010.  * Accepts: mail stream
  1011.  *        destination mailbox
  1012.  *        stringstruct of message to append
  1013.  * Returns: T on success, NIL on failure
  1014.  */
  1015.  
  1016. long mail_append (stream,mailbox,message)
  1017.     MAILSTREAM *stream;
  1018.     char *mailbox;
  1019.     STRING *message;
  1020. {
  1021.   DRIVER *factory = mail_valid (stream,mailbox,"append to mailbox",(long) NIL);
  1022.                 /* do the driver's action */
  1023.   return factory ? (factory->append) (stream,mailbox,message) : NIL;
  1024. }
  1025.  
  1026. /* Mail garbage collect stream
  1027.  * Accepts: mail stream
  1028.  *        garbage collection flags
  1029.  */
  1030.  
  1031. void mail_gc (stream,gcflags)
  1032.     MAILSTREAM *stream;
  1033.     long gcflags;
  1034. {
  1035.   unsigned long i = 1;
  1036.   LONGCACHE *lelt;
  1037.                   /* do the driver's action first */
  1038.   if (stream->dtb) (*stream->dtb->gc) (stream,gcflags);
  1039.   if (gcflags & GC_ENV) {    /* garbage collect envelopes? */
  1040.                 /* yes, free long cache if in use */
  1041.     if (!stream->scache) while (i <= stream->nmsgs)
  1042.       if (lelt = (LONGCACHE *) (*mailcache) (stream,i++,CH_LELT)) {
  1043.     mail_free_envelope (&lelt->env);
  1044.     mail_free_body (&lelt->body);
  1045.       }
  1046.     stream->msgno = 0;        /* free this cruft too */
  1047.     mail_free_envelope (&stream->env);
  1048.     mail_free_body (&stream->body);
  1049.   }
  1050.                 /* free text if any */
  1051.   if ((gcflags & GC_TEXTS) && (stream->text)) fs_give ((void **)&stream->text);
  1052. }
  1053.  
  1054. /* Mail output date from elt fields
  1055.  * Accepts: character string to write into
  1056.  *        elt to get data data from
  1057.  * Returns: the character string
  1058.  */
  1059.  
  1060. const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1061.             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1062.  
  1063. char *mail_date (string,elt)
  1064.     char *string;
  1065.     MESSAGECACHE *elt;
  1066. {
  1067.   const char *s = (elt->month && elt->month < 13) ?
  1068.     months[elt->month - 1] : (const char *) "???";
  1069.   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
  1070.        elt->day,s,elt->year + BASEYEAR,
  1071.        elt->hours,elt->minutes,elt->seconds,
  1072.        elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
  1073.   return string;
  1074. }
  1075.  
  1076.  
  1077. /* Mail output cdate format date from elt fields
  1078.  * Accepts: character string to write into
  1079.  *        elt to get data data from
  1080.  * Returns: the character string
  1081.  */
  1082.  
  1083. const char *cdays[] = {"Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon"};
  1084.  
  1085. char *mail_cdate (string,elt)
  1086.     char *string;
  1087.     MESSAGECACHE *elt;
  1088. {
  1089.   const char *s = (elt->month && elt->month < 13) ?
  1090.     months[elt->month - 1] : (const char *) "???";
  1091.   int m = elt->month;
  1092.   int y = elt->year + BASEYEAR;
  1093.   if (elt->month <= 2) {    /* if before March, */
  1094.     m = elt->month + 9;        /* January = month 10 of previous year */
  1095.     y--;
  1096.   }
  1097.   else m = elt->month - 3;    /* March is month 0 */
  1098.   sprintf (string,"%s %s %2d %02d:%02d:%02d %4d\n",
  1099.        cdays[(int)(elt->day+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100)) % 7],s,
  1100.        elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR);
  1101.   return string;
  1102. }
  1103.  
  1104. /* Mail parse date into elt fields
  1105.  * Accepts: elt to write into
  1106.  *        date string to parse
  1107.  * Returns: T if parse successful, else NIL
  1108.  * This routine accepts mm/dd/yy format for the date as well
  1109.  */
  1110.  
  1111. long mail_parse_date (elt,s)
  1112.     MESSAGECACHE *elt;
  1113.     char *s;
  1114. {
  1115.   long d,m,y;
  1116.   int ms;
  1117.   struct tm *t;
  1118.   time_t tn;
  1119.                 /* skip over possible day of week */
  1120.   if (isalpha (*s) && s[3] == ',') s += 5;
  1121.                 /* parse first number (probable month) */
  1122.   if (!(s && (m = strtol ((const char *) s,&s,10)))) return NIL;
  1123.   switch (*s) {            /* different parse based on delimiter */
  1124.   case '/':            /* mm/dd/yy format */
  1125.     if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
  1126.       (y = strtol ((const char *) ++s,&s,10)) && *s == '\0')) return NIL;
  1127.     break;
  1128.   case ' ':            /* dd mmm yy format */
  1129.   case '-':            /* dd-mmm-yy format */
  1130.     d = m;            /* so the number we got is a day */
  1131.                 /* make sure string is UC and long enough! */
  1132.     if (strlen (ucase (s)) < 5) return NIL;
  1133.     /* Some compilers don't allow `<<' and/or longs in case statements. */
  1134.                 /* slurp up the month string */
  1135.     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
  1136.     switch (ms) {        /* determine the month */
  1137.     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1138.     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1139.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1140.     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1141.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1142.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1143.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1144.     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1145.     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1146.     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
  1147.     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
  1148.     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
  1149.     default: return NIL;
  1150.     }
  1151.     if (((s[4] == '-') || s[4] == ' ') &&
  1152.     (y = strtol ((const char *) s+5,&s,10)) && (*s == '\0' || *s == ' '))
  1153.       break;            /* parse the year */
  1154.   default: return NIL;        /* unknown date format */
  1155.   }
  1156.                 /* minimal validity check of date */
  1157.   if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
  1158.                 /* Tenex/ARPAnet began in 1969 */
  1159.   if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1160.                 /* set values in elt */
  1161.   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
  1162.  
  1163.   if (*s) {            /* time specification present? */
  1164.                 /* parse time */
  1165.     d = strtol ((const char *) s,&s,10);
  1166.     if (*s != ':') return NIL;
  1167.     m = strtol ((const char *) ++s,&s,10);
  1168.     if (*s != ':') return NIL;
  1169.     y = strtol ((const char *) ++s,&s,10);
  1170.                 /* minimal validity check of time */
  1171.     if (d < 0 || d > 23 || m < 0 || m > 59 || y < 0 || y > 59) return NIL;
  1172.                 /* set values in elt */
  1173.     elt->hours = d; elt->minutes = m; elt->seconds = y;
  1174.     switch (*s) {        /* time zone specifier? */
  1175.     case '-':            /* symbolic time zone */
  1176.       if (!(ms = *++s)) return NIL;
  1177.       if (*++s) {        /* multi-character? */
  1178.     ms -= 'A'; ms *= 1024;    /* yes, make compressed three-byte form */
  1179.     ms += ((*s++ - 'A') * 32);
  1180.     if (*s) ms += *s++ - 'A';
  1181.     if (*s) return NIL;    /* more than three characters */
  1182.       }
  1183.       /* This is not intended to be a comprehensive list of all possible
  1184.        * timezone strings.  Such a list would be impractical.  Rather, this
  1185.        * listing is intended to incorporate all military, north American, and
  1186.        * a few special cases such as Japan and the major European zone names,
  1187.        * such as what might be expected to be found in a Tenex format mailbox
  1188.        * and spewed from an IMAP server.  The trend is to migrate to numeric
  1189.        * timezones which lack the flavor but also the ambiguity of the names.
  1190.        */
  1191.       switch (ms) {        /* determine the timezone */
  1192.                 /* Middle Europe */
  1193.       case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1194.       case 'A': elt->zhours = 1; break;
  1195.                 /* Eastern Europe */
  1196.       case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1197.       case 'B': elt->zhours = 2; break;
  1198.       case 'C': elt->zhours = 3; break;
  1199.       case 'D': elt->zhours = 4; break;
  1200.       case 'E': elt->zhours = 5; break;
  1201.       case 'F': elt->zhours = 6; break;
  1202.       case 'G': elt->zhours = 7; break;
  1203.       case 'H': elt->zhours = 8; break;
  1204.                 /* Japan */
  1205.       case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1206.       case 'I': elt->zhours = 9; break;
  1207.       case 'K': elt->zhours = 10; break;
  1208.       case 'L': elt->zhours = 11; break;
  1209.       case 'M': elt->zhours = 12; break;
  1210.  
  1211.       case 'N': elt->zoccident = 1; elt->zhours = 1; break;
  1212.       case 'O': elt->zoccident = 1; elt->zhours = 2; break;
  1213.       case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1214.       case 'P': elt->zoccident = 1; elt->zhours = 3; break;
  1215.                 /* Atlantic */
  1216.       case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1217.       case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1218.       case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
  1219.                 /* Eastern */
  1220.       case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1221.       case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1222.       case 'R': elt->zoccident = 1; elt->zhours = 5; break;
  1223.                 /* Central */
  1224.       case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1225.       case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1226.       case 'S': elt->zoccident = 1; elt->zhours = 6; break;
  1227.                 /* Mountain */
  1228.       case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1229.       case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1230.       case 'T': elt->zoccident = 1; elt->zhours = 7; break;
  1231.                 /* Pacific */
  1232.       case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1233.       case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1234.       case 'U': elt->zoccident = 1; elt->zhours = 8; break;
  1235.                 /* Yukon */
  1236.       case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1237.       case (('H'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1238.       case 'V': elt->zoccident = 1; elt->zhours = 9; break;
  1239.                 /* Hawaii */
  1240.       case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1241.       case (('B'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1242.       case 'W': elt->zoccident = 1; elt->zhours = 10; break;
  1243.                 /* Bering */
  1244.       case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1245.       case 'X': elt->zoccident = 1; elt->zhours = 11; break;
  1246.       case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
  1247.                 /* Universal */
  1248.       case (('U'-'A')*1024)+(('T'-'A')*32):
  1249.       case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
  1250.       case 'Z': elt->zhours = 0; break;
  1251.  
  1252.       case (('L'-'A')*1024) + (('C'-'A')*32)+'L'-'A':
  1253.     /* I knew I was going to regret that idiotic `LCL' stuff someday!! */
  1254.     tn = time (0);        /* time now... */
  1255.     t = localtime (&tn);    /* get local minutes since midnight */
  1256.     m = t->tm_hour * 60 + t->tm_min;
  1257.     t = gmtime (&tn);    /* minus UTC minutes since midnight */
  1258.                 /* add a day if too far past */
  1259.     if ((m -= t->tm_hour * 60 + t->tm_min) < -720) m += 1440;
  1260.     if (m > 720) m -= 1440;    /* subtract a day if too far ahead */
  1261.     if (m < 0) {        /* occidental? */
  1262.       m = abs (m);        /* yup, make positive number */
  1263.       elt->zoccident = 1;    /* and note west of UTC */
  1264.     }
  1265.     elt->zhours = m / 60;    /* now break into hours and minutes */
  1266.     elt->zminutes = m % 60;
  1267.     break;
  1268.       default:            /* unknown time zone name */
  1269.     return NIL;
  1270.       }
  1271.       elt->zminutes = 0;    /* never a fractional hour */
  1272.       break;
  1273.     case '\0':            /* no time zone */
  1274.       break;
  1275.     case ' ':            /* numeric time zone */
  1276.                 /* test for sign character */
  1277.       if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
  1278.       if (!(isdigit (*s) && isdigit (s[1]) && isdigit (s[2]) && isdigit (s[3]))
  1279.        || s[4]) return NIL;    /* proper timezone */
  1280.       elt->zhours = (*s - '0') * 10 + (s[1] - '0');
  1281.       elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
  1282.       break;
  1283.     default:
  1284.       return NIL;
  1285.     }
  1286.   }
  1287.   return T;
  1288. }
  1289.  
  1290. /* Mail messages have been searched out
  1291.  * Accepts: mail stream
  1292.  *        message number
  1293.  *
  1294.  * Calls external "mm_searched" function to notify main program
  1295.  */
  1296.  
  1297. void mail_searched (stream,msgno)
  1298.     MAILSTREAM *stream;
  1299.     long msgno;
  1300. {
  1301.                 /* mark as searched */
  1302.   mail_elt (stream,msgno)->searched = T;
  1303.   mm_searched (stream,msgno);    /* notify main program */
  1304. }
  1305.  
  1306.  
  1307. /* Mail n messages exist
  1308.  * Accepts: mail stream
  1309.  *        number of messages
  1310.  *
  1311.  * Calls external "mm_exists" function that notifies main program prior
  1312.  * to updating the stream
  1313.  */
  1314.  
  1315. void mail_exists (stream,nmsgs)
  1316.     MAILSTREAM *stream;
  1317.     long nmsgs;
  1318. {
  1319.                 /* make sure cache is large enough */
  1320.   (*mailcache) (stream,nmsgs,CH_SIZE);
  1321.                 /* notify main program of change */
  1322.   if (!stream->silent) mm_exists (stream,nmsgs);
  1323.   stream->nmsgs = nmsgs;    /* update stream status */
  1324. }
  1325.  
  1326. /* Mail n messages are recent
  1327.  * Accepts: mail stream
  1328.  *        number of recent messages
  1329.  */
  1330.  
  1331. void mail_recent (stream,recent)
  1332.     MAILSTREAM *stream;
  1333.     long recent;
  1334. {
  1335.   stream->recent = recent;    /* update stream status */
  1336. }
  1337.  
  1338.  
  1339. /* Mail message n is expunged
  1340.  * Accepts: mail stream
  1341.  *        message #
  1342.  *
  1343.  * Calls external "mm_expunged" function that notifies main program prior
  1344.  * to updating the stream
  1345.  */
  1346.  
  1347. void mail_expunged (stream,msgno)
  1348.     MAILSTREAM *stream;
  1349.     long msgno;
  1350. {
  1351.   long i = msgno - 1;
  1352.   MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
  1353.   if (elt) {            /* if an element is there */
  1354.     elt->msgno = 0;        /* invalidate its message number and free */
  1355.     (*mailcache) (stream,msgno,CH_FREE);
  1356.   }
  1357.                 /* expunge the slot */
  1358.   (*mailcache) (stream,msgno,CH_EXPUNGE);
  1359.   --stream->nmsgs;        /* update stream status */
  1360.                 /* notify main program of change */
  1361.   if (!stream->silent) mm_expunged (stream,msgno);
  1362. }
  1363.  
  1364. /* mail stream status routines */
  1365.  
  1366.  
  1367. /* Mail lock stream
  1368.  * Accepts: mail stream
  1369.  */
  1370.  
  1371. void mail_lock (stream)
  1372.     MAILSTREAM *stream;
  1373. {
  1374.   if (stream->lock) fatal ("Lock when already locked");
  1375.   else stream->lock = T;    /* lock stream */
  1376. }
  1377.  
  1378.  
  1379. /* Mail unlock stream
  1380.  * Accepts: mail stream
  1381.  */
  1382.  
  1383. void mail_unlock (stream)
  1384.     MAILSTREAM *stream;
  1385. {
  1386.   if (!stream->lock) fatal ("Unlock when not locked");
  1387.   else stream->lock = NIL;    /* unlock stream */
  1388. }
  1389.  
  1390.  
  1391. /* Mail turn on debugging telemetry
  1392.  * Accepts: mail stream
  1393.  */
  1394.  
  1395. void mail_debug (stream)
  1396.     MAILSTREAM *stream;
  1397. {
  1398.   stream->debug = T;        /* turn on debugging telemetry */
  1399. }
  1400.  
  1401.  
  1402. /* Mail turn off debugging telemetry
  1403.  * Accepts: mail stream
  1404.  */
  1405.  
  1406. void mail_nodebug (stream)
  1407.     MAILSTREAM *stream;
  1408. {
  1409.   stream->debug = NIL;        /* turn off debugging telemetry */
  1410. }
  1411.  
  1412. /* Mail parse sequence
  1413.  * Accepts: mail stream
  1414.  *        sequence to parse
  1415.  * Returns: T if parse successful, else NIL
  1416.  */
  1417.  
  1418. long mail_sequence (stream,sequence)
  1419.     MAILSTREAM *stream;
  1420.     char *sequence;
  1421. {
  1422.   long i,j,x;
  1423.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  1424.   while (*sequence) {        /* while there is something to parse */
  1425.                 /* parse and validate message number */
  1426.     if (((i = (int) strtol ((const char *) sequence,&sequence,10)) < 1) ||
  1427.     (i > stream->nmsgs)) {
  1428.       mm_log ("Sequence invalid",ERROR);
  1429.       return NIL;
  1430.     }
  1431.     switch (*sequence) {    /* see what the delimiter is */
  1432.     case ':':            /* sequence range */
  1433.                 /* parse end of range */
  1434.       if (((j = (int) strtol ((const char *) ++sequence,&sequence,10)) < 1) ||
  1435.       (j > stream->nmsgs) || (*sequence && *sequence++ != ',')) {
  1436.     mm_log ("Sequence range invalid",ERROR);
  1437.     return NIL;
  1438.       }
  1439.       if (i > j) {        /* swap the range if backwards */
  1440.     x = i; i = j; j = x;
  1441.       }
  1442.                 /* mark each item in the sequence */
  1443.       while (i <= j) mail_elt (stream,j--)->sequence = T;
  1444.       break;
  1445.     case ',':            /* single message */
  1446.       ++sequence;        /* skip the delimiter, fall into end case */
  1447.     case '\0':            /* end of sequence, mark this message */
  1448.       mail_elt (stream,i)->sequence = T;
  1449.       break;
  1450.     default:            /* anything else is a syntax error! */
  1451.       mm_log ("Syntax error in sequence",ERROR);
  1452.       return NIL;
  1453.     }
  1454.   }
  1455.   return T;            /* successfully parsed sequence */
  1456. }
  1457.  
  1458. /* Mail data structure instantiation routines */
  1459.  
  1460.  
  1461. /* Mail instantiate envelope
  1462.  * Returns: new envelope
  1463.  */
  1464.  
  1465. ENVELOPE *mail_newenvelope ()
  1466. {
  1467.   ENVELOPE *env = (ENVELOPE *) fs_get (sizeof (ENVELOPE));
  1468.   env->remail = NIL;        /* initialize all fields */
  1469.   env->return_path = NIL;
  1470.   env->date = NIL;
  1471.   env->subject = NIL;
  1472.   env->from = env->sender = env->reply_to = env->to = env->cc = env->bcc = NIL;
  1473.   env->in_reply_to = env->message_id = env->newsgroups = NIL;
  1474.   return env;
  1475. }
  1476.  
  1477.  
  1478. /* Mail instantiate address
  1479.  * Returns: new address
  1480.  */
  1481.  
  1482. ADDRESS *mail_newaddr ()
  1483. {
  1484.   ADDRESS *adr = (ADDRESS *) fs_get (sizeof (ADDRESS));
  1485.                 /* initialize all fields */
  1486.   adr->personal = adr->adl = adr->mailbox = adr->host = adr->error = NIL;
  1487.   adr->next = NIL;
  1488.   return adr;
  1489. }
  1490.  
  1491.  
  1492. /* Mail instantiate body
  1493.  * Returns: new body
  1494.  */
  1495.  
  1496. BODY *mail_newbody ()
  1497. {
  1498.   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
  1499. }
  1500.  
  1501. /* Mail initialize body
  1502.  * Accepts: body
  1503.  * Returns: body
  1504.  */
  1505.  
  1506. BODY *mail_initbody (body)
  1507.     BODY *body;
  1508. {
  1509.   body->type = TYPETEXT;    /* content type */
  1510.   body->encoding = ENC7BIT;    /* content encoding */
  1511.   body->subtype = body->id = body->description = NIL;
  1512.   body->parameter = NIL;
  1513.   body->contents.text = NIL;    /* no contents yet */
  1514.   body->contents.binary = NIL;
  1515.   body->contents.part = NIL;
  1516.   body->contents.msg.env = NIL;
  1517.   body->contents.msg.body = NIL;
  1518.   body->contents.msg.text = NIL;
  1519.   body->size.lines = body->size.bytes = body->size.ibytes = 0;
  1520.   return body;
  1521. }
  1522.  
  1523.  
  1524. /* Mail instantiate body parameter
  1525.  * Returns: new body part
  1526.  */
  1527.  
  1528. PARAMETER *mail_newbody_parameter ()
  1529. {
  1530.   PARAMETER *parameter = (PARAMETER *) fs_get (sizeof (PARAMETER));
  1531.   parameter->attribute = parameter->value = NIL;
  1532.   parameter->next = NIL;    /* no next yet */
  1533.   return parameter;
  1534. }
  1535.  
  1536.  
  1537. /* Mail instantiate body part
  1538.  * Returns: new body part
  1539.  */
  1540.  
  1541. PART *mail_newbody_part ()
  1542. {
  1543.   PART *part = (PART *) fs_get (sizeof (PART));
  1544.   mail_initbody (&part->body);    /* initialize the body */
  1545.   part->offset = 0;        /* no offset yet */
  1546.   part->next = NIL;        /* no next yet */
  1547.   return part;
  1548. }
  1549.  
  1550. /* Mail garbage collection routines */
  1551.  
  1552.  
  1553. /* Mail garbage collect body
  1554.  * Accepts: pointer to body pointer
  1555.  */
  1556.  
  1557. void mail_free_body (body)
  1558.     BODY **body;
  1559. {
  1560.   if (*body) {            /* only free if exists */
  1561.     mail_free_body_data (*body);/* free its data */
  1562.     fs_give ((void **) body);    /* return body to free storage */
  1563.   }
  1564. }
  1565.  
  1566.  
  1567. /* Mail garbage collect body data
  1568.  * Accepts: body pointer
  1569.  */
  1570.  
  1571. void mail_free_body_data (body)
  1572.     BODY *body;
  1573. {
  1574.   if (body->subtype) fs_give ((void **) &body->subtype);
  1575.   mail_free_body_parameter (&body->parameter);
  1576.   if (body->id) fs_give ((void **) &body->id);
  1577.   if (body->description) fs_give ((void **) &body->description);
  1578.   switch (body->type) {        /* free contents */
  1579.   case TYPETEXT:        /* unformatted text */
  1580.     if (body->contents.text) fs_give ((void **) &body->contents.text);
  1581.     break;
  1582.   case TYPEMULTIPART:        /* multiple part */
  1583.     mail_free_body_part (&body->contents.part);
  1584.     break;
  1585.   case TYPEMESSAGE:        /* encapsulated message */
  1586.     mail_free_envelope (&body->contents.msg.env);
  1587.     mail_free_body (&body->contents.msg.body);
  1588.     if (body->contents.msg.text)
  1589.       fs_give ((void **) &body->contents.msg.text);
  1590.     break;
  1591.   case TYPEAPPLICATION:        /* application data */
  1592.   case TYPEAUDIO:        /* audio */
  1593.   case TYPEIMAGE:        /* static image */
  1594.   case TYPEVIDEO:        /* video */
  1595.     if (body->contents.binary) fs_give (&body->contents.binary);
  1596.     break;
  1597.   default:
  1598.     break;
  1599.   }
  1600. }
  1601.  
  1602. /* Mail garbage collect body parameter
  1603.  * Accepts: pointer to body parameter pointer
  1604.  */
  1605.  
  1606. void mail_free_body_parameter (parameter)
  1607.     PARAMETER **parameter;
  1608. {
  1609.   if (*parameter) {        /* only free if exists */
  1610.     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
  1611.     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
  1612.                 /* run down the list as necessary */
  1613.     mail_free_body_parameter (&(*parameter)->next);
  1614.                 /* return body part to free storage */
  1615.     fs_give ((void **) parameter);
  1616.   }
  1617. }
  1618.  
  1619.  
  1620. /* Mail garbage collect body part
  1621.  * Accepts: pointer to body part pointer
  1622.  */
  1623.  
  1624. void mail_free_body_part (part)
  1625.     PART **part;
  1626. {
  1627.   if (*part) {            /* only free if exists */
  1628.     mail_free_body_data (&(*part)->body);
  1629.                 /* run down the list as necessary */
  1630.     mail_free_body_part (&(*part)->next);
  1631.     fs_give ((void **) part);    /* return body part to free storage */
  1632.   }
  1633. }
  1634.  
  1635. /* Mail garbage collect message cache
  1636.  * Accepts: mail stream
  1637.  *
  1638.  * The message cache is set to NIL when this function finishes.
  1639.  */
  1640.  
  1641. void mail_free_cache (stream)
  1642.     MAILSTREAM *stream;
  1643. {
  1644.                 /* flush the cache */
  1645.   (*mailcache) (stream,(long) 0,CH_INIT);
  1646.   stream->msgno = 0;        /* free this cruft too */
  1647.   mail_free_envelope (&stream->env);
  1648.   mail_free_body (&stream->body);
  1649.   if (stream->text) fs_give ((void **) &stream->text);
  1650. }
  1651.  
  1652.  
  1653. /* Mail garbage collect cache element
  1654.  * Accepts: pointer to cache element pointer
  1655.  */
  1656.  
  1657. void mail_free_elt (elt)
  1658.     MESSAGECACHE **elt;
  1659. {
  1660.                 /* only free if exists and no sharers */
  1661.   if (*elt && !--(*elt)->lockcount) fs_give ((void **) elt);
  1662.   else *elt = NIL;        /* else simply drop pointer */
  1663. }
  1664.  
  1665.  
  1666. /* Mail garbage collect long cache element
  1667.  * Accepts: pointer to long cache element pointer
  1668.  */
  1669.  
  1670. void mail_free_lelt (lelt)
  1671.     LONGCACHE **lelt;
  1672. {
  1673.                 /* only free if exists and no sharers */
  1674.   if (*lelt && !--(*lelt)->elt.lockcount) {
  1675.     mail_free_envelope (&(*lelt)->env);
  1676.     mail_free_body (&(*lelt)->body);
  1677.     fs_give ((void **) lelt);    /* return cache element to free storage */
  1678.   }
  1679.   else *lelt = NIL;        /* else simply drop pointer */
  1680. }
  1681.  
  1682. /* Mail garbage collect envelope
  1683.  * Accepts: pointer to envelope pointer
  1684.  */
  1685.  
  1686. void mail_free_envelope (env)
  1687.     ENVELOPE **env;
  1688. {
  1689.   if (*env) {            /* only free if exists */
  1690.     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
  1691.     mail_free_address (&(*env)->return_path);
  1692.     if ((*env)->date) fs_give ((void **) &(*env)->date);
  1693.     mail_free_address (&(*env)->from);
  1694.     mail_free_address (&(*env)->sender);
  1695.     mail_free_address (&(*env)->reply_to);
  1696.     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
  1697.     mail_free_address (&(*env)->to);
  1698.     mail_free_address (&(*env)->cc);
  1699.     mail_free_address (&(*env)->bcc);
  1700.     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
  1701.     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
  1702.     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
  1703.     fs_give ((void **) env);    /* return envelope to free storage */
  1704.   }
  1705. }
  1706.  
  1707.  
  1708. /* Mail garbage collect address
  1709.  * Accepts: pointer to address pointer
  1710.  */
  1711.  
  1712. void mail_free_address (address)
  1713.     ADDRESS **address;
  1714. {
  1715.   if (*address) {        /* only free if exists */
  1716.     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
  1717.     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
  1718.     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
  1719.     if ((*address)->host) fs_give ((void **) &(*address)->host);
  1720.     if ((*address)->error) fs_give ((void **) &(*address)->error);
  1721.     mail_free_address (&(*address)->next);
  1722.     fs_give ((void **) address);/* return address to free storage */
  1723.   }
  1724. }
  1725.